package com.bluexmicro.moduletester

import android.annotation.SuppressLint
import android.bluetooth.le.ScanResult
import android.content.Context
import android.util.Log
import android.widget.Toast
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.bluexmicro.android.coroutineble.BluetoothPeripheral
import com.bluexmicro.android.coroutineble.STATE_READY_SCAN_CONNECT
import com.bluexmicro.android.coroutineble.checkBluetoothState
import com.bluexmicro.android.coroutineble.model.BleResponse
import com.bluexmicro.android.coroutineble.scanPeripheral
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import kotlinx.coroutines.withTimeout
import java.util.UUID

const val uuid_service = "c2e6fba0-e966-1000-8000-bef9c223df6a"
const val uuid_characteristic_tx = "c2e6fba1-e966-1000-8000-bef9c223df6a"
const val uuid_characteristic_rx = "c2e6fba2-e966-1000-8000-bef9c223df6a"

data class MyEntry(
    val address: String,
    var tx: Boolean?,
    var rx: Boolean?
) {
    fun isSuccess(): Boolean {
        return tx == true && rx == true
    }
}

class TestViewModel : ViewModel() {

    private val _testing = MutableStateFlow(false)
    val testing: StateFlow<Boolean> = _testing

    ///////////////////////////////////////////////////////////////////////////
    // 过滤
    ///////////////////////////////////////////////////////////////////////////
    val signal = mutableStateOf(0f)//0~127
    val companies = mutableStateListOf<Short>()
    val names = mutableStateListOf<String>()

    ///////////////////////////////////////////////////////////////////////////
    // 测试
    ///////////////////////////////////////////////////////////////////////////

    private val _currentDevice = MutableStateFlow<MyEntry?>(null)
    val currentDevice: StateFlow<MyEntry?> = _currentDevice

    //这里是已经处理过的设备
    private val cache = mutableMapOf<String, MyEntry>()

    private val _success = MutableStateFlow<List<MyEntry>>(emptyList())
    private val _failed = MutableStateFlow<List<MyEntry>>(emptyList())

    val successList: StateFlow<List<MyEntry>> = _success
    val failedList: StateFlow<List<MyEntry>> = _failed

    fun clearRecords() {
        cache.clear()
        _success.value = emptyList()
        _failed.value = emptyList()
    }


    @SuppressLint("MissingPermission")
    private fun matchAndUntested(scanResult: ScanResult): Boolean {
        //判断信号强度
        if ((scanResult.rssi + 127) < signal.value) return false

        //判断名字 或者广播名字
        val name = scanResult.device.name ?: ""
        val localName = scanResult.scanRecord?.deviceName ?: ""
        if (names.isNotEmpty()) {
            names.find { name.contains(name, true) || localName.contains(localName, true) }
                ?: return false
        }

        //判断公司Id
        if (companies.isNotEmpty()) {
            companies.find {
                scanResult.scanRecord?.getManufacturerSpecificData(it.toInt()) != null
            } ?: return false
        }

        //已经测试过
        if (cache.contains(scanResult.device.address)) {
            return false
        }
        return scanResult.scanRecord?.getManufacturerSpecificData(0x09b3) != null
//        return true
    }

    /**
     * 待处理的
     */
    private var pendingList = emptyList<ScanResult>()
    private var scanJob: Job? = null
    private fun startScan(context: Context) {
        stopScan()
        scanJob = viewModelScope.launch {
            context.scanPeripheral(reportDelay = 300).catch {
                _testing.value = false
                job?.takeIf { it.isActive }?.apply { cancel() }
            }.collect {
                Log.e("TAG", "startScan: ${it.size}")
                pendingList = it.filter { res ->
                    //过滤已测试的和正在测试的
                    matchAndUntested(res) && res.device.address != currentDevice.value?.address
                }
            }
        }
    }

    private fun stopScan() {
        scanJob?.takeIf { it.isActive }?.apply { cancel() }
    }


    private var job: Job? = null
    fun toggle(context: Context) {

        _testing.value = _testing.value.not()
        if (_testing.value) {
            if (isReady(context)) {
                startScan(context)
                execute(context)
            } else {
                stopScan()
                _testing.value = false
                Toast.makeText(context, "蓝牙异常", Toast.LENGTH_LONG).show()
            }
        } else {
            job?.takeIf { it.isActive }?.apply { cancel() }
        }
    }

    private fun isReady(context: Context): Boolean {
        return context.checkBluetoothState() and STATE_READY_SCAN_CONNECT == STATE_READY_SCAN_CONNECT
    }


    private fun execute(context: Context) {
        job?.takeIf { it.isActive }?.apply { cancel() }
        job = viewModelScope.launch {
            //循环任务
            while (_testing.value && isReady(context)) {
//                //1. 扫描
//                val wrapped = context.scanPeripheral(reportDelay = 500)
//                    .first { results ->
//                        //符合过滤条件且不在成功列表里面
//                        results.find { res ->
//                            matchAndUntested(res)
//                        } != null
//                    }
//                val target =
//                    wrapped.find { matchAndUntested(it) } ?: continue//这个 continue 不会执行，因为一定符合条件
                val copyOfPendingList = pendingList.toList()

                if (copyOfPendingList.isEmpty()) {
                    delay(500)
                    continue
                }
                val target = copyOfPendingList.find { matchAndUntested(it) }
                if (target == null) {
                    delay(500)
                    continue
                }
                //2. 连接
                val peripheral = BluetoothPeripheral(context, target.device)
                if (!peripheral.connect()) {
                    peripheral.release()
                    continue
                }
                val services = peripheral.discoverServices()
                val service = services.find { it.uuid.equals(UUID.fromString(uuid_service)) }
                // 外设 -> 手机
                val txCharacteristic = service?.getCharacteristic(
                    UUID.fromString(uuid_characteristic_tx)
                )
                // 手机 -> 外设
                val rxCharacteristic = service?.getCharacteristic(
                    UUID.fromString(uuid_characteristic_rx)
                )
                if (txCharacteristic == null || rxCharacteristic == null) {
                    peripheral.release()
                    continue
                }

                val currentEntry = MyEntry(target.device.address, null, null)
                _currentDevice.value = currentEntry

                val ready = peripheral.toggleNotification(txCharacteristic, true)
                if (ready.not()) {
                    currentEntry.tx = false
                }

                val rxFlag = peripheral.write(rxCharacteristic, byteArrayOf(1, 2, 3))
                currentEntry.rx = rxFlag

                val rsp = try {
                    withTimeout(2000) {
                        return@withTimeout peripheral.responseShared.first {
                            it is BleResponse.OnCharacteristicChanged
                        }
                    }
                } catch (e: Exception) {
                    Log.e("TAG", "execute: $e")
                    null
                }
                currentEntry.tx = rsp != null

                cache[target.device.address] = currentEntry
                _currentDevice.value = null

                val tmp1 = mutableListOf<MyEntry>()
                val tmp2 = mutableListOf<MyEntry>()
                for (e in cache.values) {
                    if (e.isSuccess()) {
                        tmp1.add(e)
                    } else {
                        tmp2.add(e)
                    }
                }
                _success.value = tmp1
                _failed.value = tmp2
                peripheral.release()
            }//end while

            //任务终止，初始化
            _testing.value = false
        }
    }


}